<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
<head>
  <link href="//gmpg.org/xfn/11" rel="profile">
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="generator" content="Hugo 0.68.3" />

  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>redis乐观锁 &middot; 我的博客</title>

  
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/print.css" media="print">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/poole.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/syntax.css">
  <link type="text/css" rel="stylesheet" href="/my_technology_blog/css/hyde.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Abril+Fatface|PT+Sans:400,400i,700">


  
  <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
  <link rel="shortcut icon" href="/favicon.png">

  
  
</head>

  <body class="theme-base-0b ">
  <aside class="sidebar">
  <div class="container sidebar-sticky">
    <div class="sidebar-about">
      <a href="/my_technology_blog/"><h1>我的博客</h1></a>
      <p class="lead">
       杨博的博客 
      </p>
    </div>

    <nav>
      <ul class="sidebar-nav">
        <li><a href="/my_technology_blog/">Home</a> </li>
        
      </ul>
    </nav>

    <p>&copy; 2021. All rights reserved. </p>
  </div>
</aside>

    <main class="content container">
    <div class="post">
  <h1>redis乐观锁</h1>
  <time datetime=2020-06-17T15:49:27&#43;0800 class="post-date">Wed, Jun 17, 2020</time>
  <h3 id="什么是乐观锁">什么是乐观锁</h3>
<p>乐观锁（ Optimistic Locking ） 相对悲观锁而言，乐观锁假设认为数据一般情况下不会造成冲突，所以在数据进行提交更新的时候，才会正式对数据的冲突与否进行检测，如果发现冲突了，则让返回用户错误的信息，让用户决定如何去做。一般用于秒杀、抢购防止超卖这些功能中。</p>
<h3 id="如何实现redis乐观锁">如何实现redis乐观锁</h3>
<p>首先，看看redis的事务，涉及到的指令，主要有multi，exec，discard。而实现乐观锁的指令，在事务基础上，主要是watch指令，以及unwatch指令，unwatch通常可以不用！</p>
<p>1.multi，开启Redis的事务，置客户端为事务态。
2.exec，提交事务，执行从multi到此命令前的命令队列，置客户端为非事务态。
3.discard，取消事务，置客户端为非事务态。
4.watch,监视键值对，作用时如果事务提交exec时发现监视的监视对发生变化，事务将被取消。</p>
<p>案例1：redis的纯事务</p>
<p>下面是ssh窗口1里面的操作：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> set hello <span style="color:#ae81ff">1</span>
OK
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> get hello
<span style="color:#e6db74">&#34;1&#34;</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> multi
OK
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello
QUEUED
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello                 <span style="color:#75715e">#这一步执行完毕后，去另外一个窗口（ssh窗口2），对hello这个key做incr操作，将hello对应的值变成2。完成后，继续后面的exec指令</span>
QUEUED
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">exec</span>
<span style="color:#ae81ff">1</span>) (integer) <span style="color:#ae81ff">3</span>                             <span style="color:#75715e">#注意，这时hello的值是3了，前面执行get hello指令时，值是1哟，说明这个值在其他地方被修改过，这里的其他地方，就是指前面提到的，在另外一个连接窗口里面执行的。</span>
<span style="color:#ae81ff">2</span>) (integer) <span style="color:#ae81ff">4</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span>
</code></pre></div><p>这个情景下，multi和exec之间的指令，依然是可以执行的。</p>
<p>下面的操作，就是在ssh窗口2里面的操作：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> 
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> get hello
<span style="color:#e6db74">&#34;1&#34;</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello
(integer) <span style="color:#ae81ff">2</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span>
</code></pre></div><p><strong>案例2： 利用watch指令，基于CAS机制，简单的乐观锁</strong></p>
<p>下面是ssh窗口1里面的操作：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> watch hello
OK
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> get hello
<span style="color:#e6db74">&#34;4&#34;</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> multi
OK
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello
QUEUED
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello                <span style="color:#75715e">#这一步执行完毕后，去另外一个窗口（ssh窗口2），对hello这个key做incr操作，将其值变成5。完成后，继续后面的exec指令</span>
QUEUED
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">exec</span>
(nil)                                     <span style="color:#75715e">#注意，这是exec执行后返回的是nil，表示事务提交执行失败</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> 
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> get hello                 <span style="color:#75715e">#这个时候，查看hello对应的值，就是在另外一个窗口（ssh窗口2）执行incr后的值</span>
<span style="color:#e6db74">&#34;5&#34;</span>
</code></pre></div><p>下面是ssh窗口2里面的操作：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello
(integer) <span style="color:#ae81ff">5</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> 
</code></pre></div><p><strong>案例3：watch指令在一次事务执行完毕后，即结束其生命周期</strong></p>
<p>下面是ssh窗口1里面的操作：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> multi                     <span style="color:#75715e">#接着上面案例2后，不再输入watch hello这个指令，直接启动事务</span>
OK
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello
QUEUED
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello                <span style="color:#75715e">#这一步执行完毕后，就在另外一个窗口（ssh窗口2），执行incr hello，将hello的值变成6。</span>
QUEUED
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">exec</span>                      <span style="color:#75715e">#另外一个窗口（ssh窗口2）里面的操作结束后，继续来这个窗口执行该指令，依然完成了上面的两个incr hello的操作。</span>
<span style="color:#ae81ff">1</span>) (integer) <span style="color:#ae81ff">7</span>
<span style="color:#ae81ff">2</span>) (integer) <span style="color:#ae81ff">8</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span>
</code></pre></div><p>下面是ssh窗口2里面的操作：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python"><span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span> incr hello
(integer) <span style="color:#ae81ff">6</span>
<span style="color:#ae81ff">127.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0.1</span>:<span style="color:#ae81ff">6379</span><span style="color:#f92672">&gt;</span>
</code></pre></div><p>上述3个案例的操作，指令其实非常的少，两个窗口的指令全集，截图如下：</p>
<p>通过这个简单的例子，基于redis的乐观锁，可以得出一个结论：</p>
<ol>
<li>
<p>乐观锁的实现，必须基于WATCH，然后利用redis的事务。</p>
</li>
<li>
<p>WATCH生命周期，只是和事务关联的，一个事务执行完毕，相应的watch的生命周期即结束。</p>
</li>
</ol>

</div>


    </main>

    
  </body>
</html>
